/*
 * @(#)NetworkSession.java 1.0 2009/01/25
 */
package stardust.core.network;


/**
 * <code>NetworkSession</code> 抽象类可维护一个网络连接的完整生命周期，并提供统一的数据传输的接口。
 * <p/>
 * @see NetworkState
 * @version 1.0, 2009/01/25
 * @author ChenChang
 */
public abstract class NetworkSession
{

  /**
   * 返回指定协议的网络会话服务提供对象。
   * <p/>
   * @param type
* 指定的协议类型，它是一个 {@link ProtocolType} 枚举类型。
   * <p/>
   * @return 返回指定协议的服务提供对象。
   */
  public static NetworkSession getInstance(ProtocolType type)
  {
    if (type == ProtocolType.tcp)
    {
      return new TCPNetworkSession();
    }
    else if (type == ProtocolType.secureTcp)
    {
      // return new SecureTCPNetworkSession();
    }
    else if (type == ProtocolType.udp)
    {
      return new UDPNetworkSession();
    }

    return null;
  }

  /**
   * 初始化网络会话。
   */
  private NetworkSession()
  {
    this.state = NetworkState.closed;
  }

  /**
   * 使用特定的协议类型在指定端口上立即进行侦听。
   * <p/>
   * @param type
* 要使用的传输协议类型。
   */
  public NetworkSession(ProtocolType type)
  {
    this();
    this.type = type;
  }

  /**
   * 网络会话的状态。
   */
  protected NetworkState state;

  /**
   * 获取网络会话的状态。
   */
  public abstract NetworkState getState();

  /**
   * 存储网络会话的底层协议类型。
   */
  private ProtocolType type;

  /**
   * 获取网络会话的底层协议类型。
   * <p/>
   * @return 返回协议类型，它是一个 {@link ProtocolType} 枚举。
   */
  public ProtocolType getType()
  {
    return this.type;
  }

  /**
   * 在参数中指定的端口立即开始侦听动作，当侦听动作开始需要调用
   * <code>#accpet</code> 方法来建立网络会话。
   * <p/>
   * @param address
* 要侦听的地址，为 <tt>null</tt> 时表示侦听本地所有地址。
   * @param port
   * 要侦听的端口，取值范围在 1～65535 之间。
   * <p/>
   * @throws NetworkException
* 在网络异常时抛出此异常，如端口已被占用或被防火墙拦截等。
   */
  public abstract void listen(String address, int port) throws NetworkException;

  /**
   * 接受一个连接请求。
   * <p/>
   * @param timeout
* 参数timeout 指定如果在等待指定时间后队列内仍然没有连接请求将返回 <tt>null</tt> 值，小于或等于 0
   * 时表示无限期等待。
   * <p/>
   * @return 返回一个已连接成功的网络会话，如果等待超时返回 <tt>null</tt> 值。
   */
  public abstract NetworkSession accept(int timeout) throws NetworkException;

  /**
   * 通过主机地址和端口信息建立一个网络会话连接，如果连接失败会抛出 {@link NetwrokException} 并将当前状态重置为
   * {@link NetworkState.error} 状态。
   * <p/>
   * @param address
* 要连接到的目标主机地址。
   * @param port
   * 要连接到的目标主机端口。
   * <p/>
   * @throws NetworkException
* 连接到目标主机失败时抛出这个异常。
   * @see disconnect
   */
  public abstract void connect(String address, int port) throws NetworkException;

  /**
   * 断开已建立的连接或停止当前的侦听动作，并重置当前会话状态为 {@link NetworkState.closed} 状态。
   */
  public abstract void disconnect();

  public abstract String getRemoteHost();

  public abstract Integer getRemotePort();

  public abstract String getLocalHost();

  public abstract Integer getLocalPort();

  /**
   * 发送数据，缓冲机制和传输可靠性按照提供的网络配置来处理。
   * <p/>
   * @param data
  * 要发送的字节数据数组。
   * @param offset
* 数组中的起始位置。
   * @param len
   * 数组从起始位置开始的长度。
   */
  public abstract int write(byte[] data, int offset, int len) throws NetworkException;

  /**
   * 发送数据，缓冲机制和传输可靠性按照提供的网络配置来处理。
   * <p/>
   * @param data
* 要发送的字节数据。
   */
  public abstract int write(byte data) throws NetworkException;

  /**
   * 读取数据，缓冲机制和传输可靠性按照提供的网络配置来处理。
   * <p/>
   * @param data
  * 要读取到的字节数据数组。
   * @param offset
* 数组中的起始位置。
   * @param len
   * 数组从起始位置开始的长度。
   */
  public abstract int read(byte[] data, int offset, int len) throws NetworkException;

  /**
   * 读取数据，缓冲机制和传输可靠性按照提供的网络配置来处理。
   */
  public abstract int read() throws NetworkException;

  /**
   * 立即应用当前网络配置的更新。
   */
  public abstract void apply();

  /**
   * 存储网络会话配置，由 {@link apply} 方法使用。
   */
  protected NetworkConfiguration configuration = new NetworkConfiguration(this);

  public NetworkConfiguration getConfiguration()
  {
    return this.configuration;
  }

  public static class NetworkConfiguration
  {

    public static final int DEFAULT_PACKET_SIZE = 1024;

    private NetworkSession session;

    private int timeout = 1000 * 10;

    private int packetSize;

    public NetworkConfiguration(NetworkSession session)
    {
      this.session = session;
      this.timeout = 0;
      this.packetSize = DEFAULT_PACKET_SIZE;

      this.session.apply();
    }

    public void setTimeout(int timeout)
    {
      this.timeout = timeout;
      this.session.apply();
    }

    public int getTimeout()
    {
      return timeout;
    }

    public void setPacketSize(int packetSize)
    {
      this.packetSize = packetSize;
      this.session.apply();
    }

    public int getPacketSize()
    {
      return packetSize;
    }

  }

}
